package com.link.base.core.basic.interceptors.mybatis.system;

import com.alibaba.fastjson.JSONObject;
import com.link.base.base.trackchild.model.Trackchild;
import com.link.base.base.trackdisplay.model.TrackDisplay;
import com.link.base.base.trackhead.model.Trackhead;
import com.link.base.user.model.CoreUser;
import com.link.core.basic.model.BasicModel;
import com.link.core.basic.service.KeyGenerateService;
import com.link.core.basic.service.snowflake.KeyGenerateUtil;
import com.link.core.cllog.LCLogger;
import com.link.core.util.StringUtils;
import com.link.core.util.UserUtil;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Invocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: qinxuegang
 * @Description:
 * @Date: 2018/11/1 11:53
 */
@Service("insertInterceptor")
public class InsertInterceptor implements SqlInterceptor {
    private static final Logger logger = LogManager.getLogger(InsertInterceptor.class);
    @Resource
    private InterceptorUtils interceptorUtils;
    @Resource
    private JdbcTemplate jdbcTemplate;
    @Resource
    private KeyGenerateService keyGenerateService;
    /**
     * 识别sql中？
     */
    private static Pattern REPLACE_PATTERN = Pattern.compile("values");
    /**
     * 识别sql中？
     */
    private static Pattern REPLACE_WHAT = Pattern.compile("\\?");

    private BeanPropertyRowMapper<Object> rowMapper = new BeanPropertyRowMapper<>(Object.class);

    @Override
    public String doInterceptor(Invocation invocation, BoundSql boundSql) {
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        Object parameterObject = boundSql.getParameterObject();
        Statement stmt = null;
        try {
            String updateSql = boundSql.getSql();
            String rgex = "/\\*.*?\\*/";
            updateSql = InterceptorUtils.getSubUtil(updateSql, rgex).toLowerCase();
            stmt = CCJSqlParserUtil.parse(updateSql);
            Insert insert = (Insert) stmt;
            Table getTable = insert.getTable();
            List<Column> columns = insert.getColumns();
            List<String> columnNames = new ArrayList<>();
            for (Column column : columns) {
                columnNames.add(column.getColumnName());
            }
            // ExpressionList expressionList = (ExpressionList) insert.getItemsList();
            Set<String> tableNames = new HashSet<>();
            tableNames.add(getTable.getName());
            //判断更新的表中是否有需要审计追踪的
            Map<String, Object> headAndChild = interceptorUtils.getHeadAndChild(tableNames, "trackNew");
            if (headAndChild.get("trackheads") == null) {
                return boundSql.getSql();
            }
            List<Trackhead> list = (List<Trackhead>) headAndChild.get("trackheads");
            if (list == null || list.size() <= 0) {
                return boundSql.getSql();
            }
            if (headAndChild.get("trackchilds") == null
                    || ((Map<String, List<Trackchild>>) headAndChild.get("trackchilds")).get(list.get(0).getTrackCode()) == null) {
                return boundSql.getSql();
            }
            List<Trackchild> listChild = ((Map<String, List<Trackchild>>) headAndChild.get("trackchilds")).get(list.get(0).getTrackCode());
            if (listChild == null || listChild.size() <= 0) {
                return boundSql.getSql();
            }
            BasicModel model = null;
            if (parameterObject == null) {
                throw new NullPointerException(" error，parameterObject is null!");
            }
            if (!(parameterObject instanceof BasicModel)) {
                throw new IllegalArgumentException(" error，argument class must be extends BasicModel!");
            }
            model = (BasicModel) parameterObject;
            Method getId = model.getClass().getMethod("getId", model.getClass().getDeclaredClasses());
            CoreUser user = UserUtil.getUser();
            if (parameterMappings == null) {
                throw new NullPointerException(" error，parameterMappings is null!");
            }
//            Long batchNum = keyGenerateService.keyGenerate();
            //修改生成id的方式是为了适配目前的多数据源切换，使用keyGenerateService会将数据源切回为主数据源
            Long batchNum = KeyGenerateUtil.keyGenerate();
            //此处需要考虑字段在那个位置
            String temphhh = updateSql;
            List<Field> fields = InterceptorUtils.getFieldList(model.getClass());
            for (int ii = 0; ii < parameterMappings.size(); ii++) {
                Object obj = InterceptorUtils.dynamicGet(model, fields, parameterMappings.get(ii).getProperty());
                if (obj == null) {
                    temphhh = replaceFirst(null, temphhh);
                } else {
                    temphhh = replaceFirst(obj.toString(), temphhh);
                }
            }
            Matcher matcher = REPLACE_PATTERN.matcher(temphhh);
            if (!matcher.find()) {
                throw new NullPointerException(" error, is not insert!");
            }
            temphhh = temphhh.substring(matcher.start() + "values".length()).trim();
            temphhh = temphhh.substring(1, temphhh.length() - 1);
            StringBuilder tempSb = new StringBuilder("select ");
            tempSb.append(temphhh).append(" from dual");
            Statement tempStmt = null;
            tempStmt = CCJSqlParserUtil.parse(tempSb.toString());
            Select select = (Select) tempStmt;
            List<SelectItem> selectItems = ((PlainSelect) select.getSelectBody()).getSelectItems();
            StringBuilder sbSql = new StringBuilder("select ");
            for (int kk = 0; kk < selectItems.size(); kk++) {
                if (kk == 0) {
                    sbSql.append(((SelectExpressionItem) selectItems.get(kk)).getExpression()).append(" as '" + kk + "' ");
                } else {
                    sbSql.append(",").append(((SelectExpressionItem) selectItems.get(kk)).getExpression()).append(" as '" + kk + "' ");
                }
            }
            sbSql.append(" from dual");
            Map<String, Object> tempResult = jdbcTemplate.queryForMap(sbSql.toString());
            for (int i = 0; i < columnNames.size(); i++) {
                for (Trackchild trackchild : listChild) {
                    if (!trackchild.getFieldCode().equalsIgnoreCase(columnNames.get(i))) {
                        continue;
                    }
                    TrackDisplay trackDisplay = new TrackDisplay();
                    if (tempResult.get(i + "") != null && StringUtils.isNotBlank(tempResult.get(i + "").toString())) {
                        trackDisplay.setUpdInfo(tempResult.get(i + "").toString());
                    } else {
                        continue;
                    }
                    trackDisplay.setUpdObjType(list.get(0).getTrackName());
                    trackDisplay.setUpdObjCode(list.get(0).getTrackCode());
                    trackDisplay.setBatchNum(batchNum);
                    trackDisplay.setUpdObjId((Long)getId.invoke(model));
                    trackDisplay.setFieldName(trackchild.getFieldName());
                    trackDisplay.setFieldValue(trackchild.getFieldCode());
                    trackDisplay.setOperate("insert");
                    trackDisplay.setAttr1(trackchild.getFieldType());
                    trackDisplay.setAttr2(trackchild.getLovType());
                    trackDisplay.setAttr3(trackchild.getDisplayName());
                    trackDisplay.setAttr4(trackchild.getDisplayValue());
                    trackDisplay.setCorpid(user.getCorpid());
                    trackDisplay.setPostnId(user.getPostnId());
                    trackDisplay.setOrgId(user.getOrgId());
                    trackDisplay.setAttr77(user.getId());
                    logger.info("发送kafka信息");
                    logger.info(JSONObject.toJSON(trackDisplay).toString());
                    KafkaSender.sendInfo(KafkaSender.TRACK_TOPIC, JSONObject.toJSON(trackDisplay).toString());
                }
            }
        } catch (Exception e) {
            LCLogger.withException(e);
        }
        return boundSql.getSql();
    }

    /**
     * 获取insert字段对应的值
     *
     * @param updateSql 传入的sql
     * @return
     */
    public static List<Map<String, String>> getFieldValue(String updateSql) {
        String tempSql = updateSql.toLowerCase();
        Matcher matcher = REPLACE_PATTERN.matcher(tempSql);
        List<Map<String, String>> list = new ArrayList<>();
        if (!matcher.find()) {
            return null;
        }
        tempSql = tempSql.substring(matcher.start() + "values".length()).trim();
        tempSql = tempSql.substring(1, tempSql.length() - 1);
        String rgex = "\\(.*?\\)";
        List<String> stringList = InterceptorUtils.getInsertUtil(tempSql, rgex);
        if (stringList == null || stringList.size() <= 0) {
            return null;
        }
        tempSql = stringList.get(stringList.size() - 1);
        String[] val = tempSql.split(",");
        for (int i = 0, j = 0; i < val.length; i++) {
            Map<String, String> tempMap = new HashMap<>();
            if ("?".equals(val[i].trim())) {
                tempMap.put(String.valueOf(j), "?");
                list.add(tempMap);
                j = j + 1;
            } else {
                if (val[i] != null && val[i].contains("insertFunction")) { // 表明是方法
                    String temp = val[i].replace("insertFunction", stringList.get(0));
                    int num = countString(temp, "\\?");
                    tempMap.put(j + "," + num, temp);
                    j = j + num;
                    stringList.remove(0);
                } else {
                    tempMap.put("real", val[i].trim());
                }
                list.add(tempMap);
            }
        }
        return list;
    }

    /**
     * 统计一个字符串中某个字符或字符串出现的次数
     *
     * @param str 字符串
     * @param s   包含的字符串
     * @return 出现的次数
     */
    public static int countString(String str, String s) {
        int count = 0;
        String[] arr = str.split(s);
        int len = arr.length;
        if (str.endsWith(s)) {
            //如果子串在末尾
            len++;
        }
        count = len - 1;
        return count;
    }

    /**
     * 替换sql中的的？
     *
     * @param str
     * @param updateSql 传入的sql
     * @return 替换后的sql
     */
    public static String replaceFirst(String str, String updateSql) {
        if (org.apache.commons.lang3.StringUtils.isEmpty(str)) {
            str = "null";
        } else {
            str = "'" + str + "'";
        }
        Matcher m = REPLACE_WHAT.matcher(updateSql);
        String temp = m.replaceFirst(str);
        return temp;
    }
}
